支付宝架构到底有多牛?
原文链接:www.tbwork.org/2019/11/10/ant-ldc-arch/
原文标题:《浅谈双十一背后的蚂蚁LDC架构和其CAP分析》
一、背景
自2008年双十一以来,在每年双十一超大规模流量的冲击上,蚂蚁金服都会不断突破现有技术的极限。2010年双11的支付峰值为2万笔/分钟,全天1280万笔支付,这个数字到2017双11时变为了25.6万笔/秒,全天14.8亿笔。在如此之大的支付TPS背后除了削峰等锦上添花的应用级优化,最解渴最实质的招数当数基于分库分表的单元化了,蚂蚁技术称之为LDC(逻辑数据中心)。
支付宝海量支付背后最解渴的设计是啥?换句话说,实现支付宝高TPS的最关键的设计是啥? LDC是啥?LDC怎么实现异地多活和异地灾备的? CAP魔咒到底是啥?P到底怎么理解? 什么是脑裂?跟CAP又是啥关系? 什么是PAXOS,它解决了啥问题? PAXOS和CAP啥关系? PAXOS可以逃脱CAP魔咒么? Oceanbase能逃脱CAP魔咒么?
二、LDC和单元化
LDC(logic data center)是相对于传统的(Internet Data Center-IDC)提出的,逻辑数据中心所表达的中心思想是无论物理结构如何的分布,整个数据中心在逻辑上是协同和统一的。这句话暗含的是强大的体系设计,分布式系统的挑战就在于整体协同工作(可用性,分区容忍性)和统一(一致性)。
小结:分库分表解决的最大痛点是数据库单点瓶颈,这个瓶颈的产生是由现代二进制数据存储体系决定的(即I/O速度)。
单元化只是分库分表后系统部署的一种方式,这种部署模式在灾备方面也发挥了极大的优势。
2.1 系统架构演化史
这样的架构显然是有问题的,单机有着明显的单点效应,单机的容量和性能都是很局限的,而使用中小型机会带来大量的浪费。 随着业务发展,这个矛盾逐渐转变为主要矛盾,因此工程师们采用了以下架构。
分库分表不仅可以对相同的库进行拆分,还可以进行对相同的表进行拆分,对表进行拆分的方式叫做水平拆分。不同功能的表放到不同的库里,一般对应的是垂直拆分(按照业务功能进行拆分),此时一般还对应了微服务化。这种方法做到极致基本能支撑TPS在万级甚至更高的访问量了。
思考一下:
应用间其实也存在交互(比如A转账给B),也就意味着,应用不需要链接其他的数据库了,但是还需要链接其他应用。如果是常见的RPC框架如dubbo等,使用的是TCP/IP协议,那么等同于把之前与数据库建立的链接,换成与其他应用之间的链接了。为啥这样就消除瓶颈了呢?首先由于合理的设计,应用间的数据交互并不巨量,其次应用间的交互可以共享TCP链接,比如A->B之间的Socket链接可以被A中的多个线程复用,而一般的数据库如MySQL则不行,所以MySQL才需要数据库链接池。
如上图所示,但我们把整套系统打包为单元化时,每一类的数据从进单元开始就注定在这个单元被消化,由于这种彻底的隔离性,整个单元可以轻松的部署到任意机房而依然能保证逻辑上的统一。
RZone(Region Zone):直译可能有点反而不好理解。实际上就是所有可以分库分表的业务系统整体部署的最小单元。每个RZone连上数据库就可以撑起一片天空,把业务跑的溜溜的。 GZone(Global Zone):全局单元,意味着全局只有一份。部署了不可拆分的数据和服务,比如系统配置等。实际情况下,GZone异地也会部署,不过仅是用于灾备,同一时刻,只有一地GZone进行全局服务。GZone一般被RZone依赖,提供的大部分是读取服务。 CZone(City Zone):顾名思义,这是以城市为单位部署的单元。同样部署了不可拆分的数据和服务,比如用户账号服务,客户信息服务等。理论上CZone会被RZone以比访问GZone高很多的频率进行访问。CZone是基于特定的GZone场景进行优化的一种单元,它把GZone中有些有着”写读时间差现象”的数据和服务进行了的单独部署,这样RZone只需要访问本地的CZone即可,而不是访问异地的GZone。
用户流水型数据:典型的有用户的订单、用户发的评论、用户的行为记录等。这些数据都是用户行为产生的流水型数据,具备天然的用户隔离性,比如A用户的App上绝对看不到B用户的订单列表。所以此类数据非常适合分库分表后独立部署服务。 用户间共享型数据:这种类型的数据又分两类。一类共享型数据是像账号、个人博客等可能会被所有用户请求访问的用户数据,比如A向B转账,A给B发消息,这时候需要确认B账号是否存在;又比如A想看B的个人博客之类的。另外一类是用户无关型数据,像商品、系统配置(汇率、优惠政策)、财务统计等这些非用户纬度的数据,很难说跟具体的某一类用户挂钩,可能涉及到所有用户。比如商品,假设按商品所在地来存放商品数据(这需要双维度分库分表),那么上海的用户仍然需要访问杭州的商品,这就又构成跨地跨zone访问了,还是达不到单元化的理想状态,而且双维度分库分表会给整个LDC运维带来复杂度提升。
注:网上和支付宝内部有另外一些分法,比如流水型和状态性,有时候还会分为三类:流水型、状态型和配置型。个人觉得这些分法虽然尝试去更高层次的抽象数据分类,但实际上边界很模糊,适得其反。
即便架构师们设计了完美的CRG,但即便在蚂蚁的实际应用中,各个系统仍然存在不合理的CRG分类,尤其是CG不分的现象很常见。
三、支付宝单元化的异地多活和灾备
3.1、流量挑拨技术探秘简介
RZ1* --> b
RZ2* --> c
RZ3* --> d
3.2、支付宝灾备机制
同机房单元间灾备。 同城机房间灾备。 异地机房间灾备。
3.2.1、同机房单元间灾备
3.2.2、同城机房间灾备
整个切流配置过程分两步,首先需要将陷入灾难的机房中RZone对应的数据分区的访问权配置进行修改;假设我们的方案是由IDC-2机房的RZ2和RZ3分别接管IDC-1中的RZ0和RZ1。那么首先要做的是把数据分区a,b对应的访问权从RZ0和RZ1收回,分配给RZ2和RZ3。即将(如上图所示为初始映射):
RZ1* --> b
RZ2* --> c
RZ3* --> d
RZ1* --> /
RZ2* --> a
RZ2* --> c
RZ3* --> b
RZ3* --> d
[25-49] --> RZ1A(50%),RZ1B(50%)
[50-74] --> RZ2A(50%),RZ2B(50%)
[75-99] --> RZ3A(50%),RZ3B(50%)
[25-49] --> RZ3A(50%),RZ3B(50%)
[50-74] --> RZ2A(50%),RZ2B(50%)
[75-99] --> RZ3A(50%),RZ3B(50%)
这里可以思考下,为何先切数据库映射,再切流量呢?这是因为如果先切流量,意味着大量注定失败的请求会被打到新的正常单元上去,从而影响系统的稳定性(数据库还没准备好)。
3.2.3、异地机房间灾备
四、蚂蚁单元化架构的CAP分析
4.1、回顾CAP
4.1.1 CAP的定义
Consistency(一致性),这个理解起来很简单,就是每时每刻每个节点上的同一份数据都是一致的。这就要求任何更新都是原子的,即要么全部成功,要么全部失败。想象一下使用分布式事务来保证所有系统的原子性是多么低效的一个操作。 Availability(可用性),这个可用性看起来很容易理解,但真正说清楚的不多。我更愿意把可用性解释为:任意时刻系统都可以提供读写服务。那么举个例子,当我们用事务将所有节点锁住来进行某种写操作时,如果某个节点发生不可用的情况,会让整个系统不可用。对于分片式的NoSQL中间件集群(Redis,Memcached)来说,一旦一个分片歇菜了,整个系统的数据也就不完整了,读取宕机分片的数据就会没响应,也就是不可用了。需要说明一点,哪些选择CP的分布式系统,并不是代表可用性就完全没有了,只是可用性没有保障了。为了增加可用性保障,这类中间件往往都提供了”分片集群+复制集”的方案。 Partition tolerance(分区容忍性),这个可能也是很多文章都没说清楚的。P并不是像CA一样是一个独立的性质,它依托于CA来进行讨论。参考文献[1]中解释道:”除非整个网络瘫痪,否则任何时刻系统都能正常工作”,言下之意是小范围的网络瘫痪,节点宕机,都不会影响整个系统的CA。我感觉这个解释听着还是有点懵逼,所以个人更愿意解释为”当节点之间网络不通时(出现网络分区),可用性和一致性仍然能得到保障”。从个人角度理解,分区容忍性又分为”可用性分区容忍性”和”一致性分区容忍性”。”出现分区时会不会影响可用性”的关键在于”需不需要所有节点互相沟通协作来完成一次事务”,不需要的话是铁定不影响可用性的,庆幸的是应该不太会有分布式系统会被设计成完成一次事务需要所有节点联动,一定要举个例子的话,全同步复制技术下的Mysql是一个典型案例[2]。”出现分区时会不会影响一致性”的关键则在于出现脑裂时有没有保证一致性的方案,这对主从同步型数据库(MySQL、SQL Server)是致命的,一旦网络出现分区,产生脑裂,系统会出现一份数据两个值的状态,谁都不觉得自己是错的。需要说明的是,正常来说同一局域网内,网络分区的概率非常低,这也是为啥我们最熟悉的数据库(MySQL、SQL Server等)也是不考虑P的原因。
还有个需要说明的地方,其实分布式系统很难满足CAP的前提条件是这个系统一定是有读有写的,如果只考虑读,那么CAP很容易都满足,比如一个计算器服务,接受表达式请求,返回计算结果,搞成水平扩展的分布式,显然这样的系统没有一致性问题,网络分区也不怕,可用性也是很稳的,所以可以满足CAP。
4.1.2 CAP分析方法
if(可用性分区容忍性-A under P))
return "AP";
else if(一致性分区容忍性-C under P)
return "CP";
}
else { //分区有影响但没考虑分区情况下的容错
if(具备可用性-A && 具备一致性-C){
return AC;
}
}
4.2 水平扩展应用+单数据库实例的CAP分析
这样的系统天然具有的就是AP(可用性和分区容忍性),一方面解决了单点导致的低可用性问题,另一方面无论这些水平扩展的机器间网络是否出现分区,这些服务器都可以各自提供服务,因为他们之间不需要进行沟通。然而,这样的系统是没有一致性可言的,想象一下每个实例都可以往数据库insert和update(注意这里还没讨论到事务),那还不乱了套。
分区容忍性:先看有没有考虑分区容忍性,或者分区后是否会有影响。单台MySQL无法构成分区,要么整个系统挂了,要么就活着。 可用性分区容忍性:分区情况下,假设恰好是该节点挂了,系统也就不可用了,所以可用性分区容忍性不满足。 一致性分区容忍性:分区情况下,只要可用,单点单机的最大好处就是一致性可以得到保障。
因此这样的一个系统,个人认为只是满足了CP。A有但不出色,从这点可以看出,CAP并不是非黑即白的。包括常说的BASE[3](最终一致性)方案,其实只是C不出色,但最终也是达到一致性的,BASE在一致性上选择了退让。
4.3 水平扩展应用+主从数据库集群的CAP分析
从上图我可以看到三个数据库实例中只有一个是主库,其他是从库。一定程度上,这种架构极大的缓解了”读可用性”问题,而这样的架构一般会做读写分离来达到更高的”读可用性”,幸运的是大部分互联网场景中读都占了80%以上,所以这样的架构能得到较长时间的广泛应用。”写可用性”可以通过keepalived[4]这种HA(高可用)框架来保证主库是活着的,但仔细一想就可以明白,这种方式并没有带来性能上的可用性提升。还好,至少系统不会因为某个实例挂了就都不可用了。可用性勉强达标了,这时候的CAP分析如下:
分区容忍性:依旧先看分区容忍性,主从结构的数据库存在节点之间的通信,他们之间需要通过心跳来保证只有一个Master。然而一旦发生分区,每个分区会自己选取一个新的Master,这样就出现了脑裂,常见的主从数据库(MySQL,Oracle等)并没有自带解决脑裂的方案。所以分区容忍性是没考虑的。 一致性:不考虑分区,由于任意时刻只有一个主库,所以一致性是满足的。 可用性:不考虑分区,HA机制的存在可以保证可用性,所以可用性显然也是满足的。
4.4 蚂蚁单元化LDC架构CAP分析
4.4.1 战胜分区容忍性
某台机器宕机了,过一会儿又重启了,看起来就像失联了一段时间,像是网络不可达一样。 异地部署情况下,异地多活意味着每一地都可能会产生数据写入,而异地之间偶尔的网络延时尖刺(网络延时曲线图陡增)、网络故障都会导致小范围的网络分区产生。前文也提到过,如果一个分布式系统是部署在一个局域网内的(一个物理机房内),那么个人认为分区的概率极低,即便有复杂的拓扑,也很少会有在同一个机房里出现网络分区的情况。而异地这个概率会大大增高,所以蚂蚁的三地五中心必须需要思考这样的问题,分区容忍不能丢!同样的情况还会发生在不同ISP的机房之间(想象一下你和朋友组队玩DOTA,他在电信,你在联通)。为了应对某一时刻某个机房突发的网络延时尖刺活着间歇性失联,一个好的分布式系统一定能处理好这种情况下的一致性问题。
如果你了解过比特币或者区块链,你就知道区块链的基础理论也是PAXOS。区块链借助PAXOS对最终一致性的贡献来抵御恶意篡改。而本文涉及的分布式应用系统则是通过PAXOS来解决分区容忍性。再说本质一点,一个是抵御部分节点变坏,一个是防范部分节点失联。
4.4.2 OceanBase的CAP分析
分区容忍性:OB节点之间是有互相通信的(需要相互同步数据),所以存在分区问题,OB通过仅同步到部分节点来保证可用性。这一点就说明OB做了分区容错。 可用性分区容忍性:OB事务只需要同步到(N/2)+1个节点,允许其余的一小半节点分区(宕机、断网等),只要(N/2)+1个节点活着就是可用的。极端情况下,比如5个节点分成3份(2:2:1),那就确实不可用了,只是这种情况概率比较低。 一致性分区容忍性:分区情况下意味着部分节点失联了,一致性显然是不满足的。但通过共识算法可以保证当下只有一个值是合法的,并且最终会通过节点间的同步达到最终一致性。
所以OB仍然没有逃脱CAP魔咒,产生分区的时候它变成AP+最终一致性(C)。整体来说,它是AP的,即高可用和分区容忍。
五、结语
个人感觉本文涉及到的知识面确实不少,每个点单独展开都可以讨论半天。回到我们紧扣的主旨来看,双十一海量支付背后技术上大快人心的设计到底是啥?我想无非是以下几点:
RZone在网络分区或灾备切换时OB的防脑裂设计(PAXOS)。我们知道RZone是单脑的(读写都在一个单元对应的库),而网络分区或者灾备时热切换过程中可能会产生多个脑,OB解决了脑裂情况下的共识问题(PAXOS算法)。 基于CZone的本地读设计。这一点保证了很大一部分有着“写读时间差”现象的公共数据能被高速本地访问。 剩下的那一丢丢不能本地访问只能实时访问GZone的公共配置数据,也兴不起什么风,作不了什么浪。比如用户创建这种TPS,不会高到哪里去。再比如对于实时库存数据,可以通过“页面展示查询走应用层缓存”+“实际下单时再校验”的方式减少其GZone调用量。
六、参考文献
[1] Practice of Cloud System Administration, The: DevOps and SRE Practices for Web Services, Volume 2. Thomas A. Limoncelli, Strata R. Chalup, Christina J. Hogan.
[2] MySQL 5.7半同步复制技术. https://www.cnblogs.com/zero-gg/p/9057092.html
[3] BASE理论分析; https://www.jianshu.com/p/f6157118e54b
[4] Keepalived; https://baike.baidu.com/item/Keepalived/10346758?fr=aladdin
[5] PAXOS; https://en.wikipedia.org/wiki/Paxos_(computer_science)
[6] OceanBase支撑2135亿成交额背后的技术原理; https://www.cnblogs.com/antfin/articles/10299396.html
[7] Backup; https://en.wikipedia.org/wiki/Backup
---------- END ----------
重磅!后厂技术官-技术交流群已成立
扫码可添加后厂技术官助手,可申请加入后厂技术官大群和细分方向群,细分方向已涵盖:Java、Python、机器学习、大数据、人工智能等群。一定要备注:开发方向+地点+学校/公司+昵称(如Java开发+北京+快手+阿信),根据格式备注,可更快被通过且邀请进群
▲长按加群
扫码可添加后厂技术官助手,可申请加入后厂技术官大群和细分方向群,细分方向已涵盖:Java、Python、机器学习、大数据、人工智能等群。一定要备注:开发方向+地点+学校/公司+昵称(如Java开发+北京+快手+阿信),根据格式备注,可更快被通过且邀请进群
推荐阅读
• Fastjson在阿里内网被喷成一坨屎了? 作者回应..
• NOI金牌得主、姚班毕业生张昆玮放弃谷歌高薪回乡教二本,网友:很羡慕他
推荐一个技术号
Github实验室,由国内一线大厂专家、985博士、硕士组成的团体运营。主要分享和研究业界实用、有趣的开源项目,学习资源,开发工具,学术交流。
关注就无套路送你一份5000页Java面试最强合集PDF。
最近面试BAT,整理一份面试资料《大厂Java面试通关指北》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 BAT 领取,更多内容陆续奉上。如有收获,点个在看,诚挚感谢 明天见(。・ω・。)ノ♡
• Fastjson在阿里内网被喷成一坨屎了? 作者回应..
• NOI金牌得主、姚班毕业生张昆玮放弃谷歌高薪回乡教二本,网友:很羡慕他
推荐一个技术号
Github实验室,由国内一线大厂专家、985博士、硕士组成的团体运营。主要分享和研究业界实用、有趣的开源项目,学习资源,开发工具,学术交流。
关注就无套路送你一份5000页Java面试最强合集PDF。
最近面试BAT,整理一份面试资料《大厂Java面试通关指北》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 BAT 领取,更多内容陆续奉上。如有收获,点个在看,诚挚感谢